﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using XNOgre.Graphics;
using Microsoft.Xna.Framework;

namespace XNOgre.Core
{
    public class Entity : MovableObject
    {
        private List<SubEntity> subEntites;
        
        internal Entity(string modelName)
            :this(Root.GenerateName(typeof(Entity)), modelName)
        { }

        internal Entity(string name, RenderOperation rop)
            :base(name)
        {
            subEntites = new List<SubEntity>();
            
            SubEntity subEnt = new SubEntity();
            subEnt.Parent = this;
            subEnt.Material = (Material)this.Material;
            subEnt.RenderOperation = rop;
            subEnt.BoneTransforms = Matrix.Identity;
            subEntites.Add(subEnt);

            subEnt.UpdateBoundingBox();
        }

        internal Entity(string name, string meshName)
            : base(name)
        {
            subEntites = new List<SubEntity>();

            Model model = Root.Instance.Content.Load<Model>(meshName);
            var modelTransforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(modelTransforms);

            Vector3 max = new Vector3(float.MinValue);
            Vector3 min = new Vector3(float.MaxValue);

            foreach (var mesh in model.Meshes)
            {
                foreach (var part in mesh.MeshParts)
                {
                    RenderOperation rop = new RenderOperation();
                    rop.OperationType = PrimitiveType.TriangleList;
                    rop.IndexData = part.IndexBuffer;
                    rop.VertexData = part.VertexBuffer;
                    rop.VertexStartIndex = part.VertexOffset;
                    rop.IndexStartIndex = part.StartIndex;
                    rop.VertexCount = part.NumVertices;
                    rop.PrimCount = part.PrimitiveCount;

                    SubEntity subEnt = new SubEntity();
                    subEnt.Parent = this;
                    subEnt.Material = (Material)this.Material;
                    subEnt.RenderOperation = rop;
                    subEnt.BoneTransforms = modelTransforms[mesh.ParentBone.Index];
                    subEntites.Add(subEnt);
                }
            }

            for (int i = 0; i < subEntites.Count; i++)
            {
                subEntites[i].UpdateBoundingBox();
            }
        }
        internal override void UpdateBoundingBox()
        {
            for (int i = 0; i < subEntites.Count; i++)
            {
                subEntites[i].UpdateBoundingBox();
            }
        }
        internal override void UpdateRenderQueue(ref List<IRenderable> queue)
        {
            for (int i = 0; i < NumSubEntites; i++)
            {
                if (GetSubEntity(i).Visible == false)
                    continue;

                queue.Add(subEntites[i]);
            }
        }
        
        public SubEntity GetSubEntity(int index)
        {
            return subEntites[index];
        }
        public int NumSubEntites
        {
            get
            {
                return subEntites.Count;
            }
        }
        
        public override RenderOperation RenderOperation
        {
            get
            {
                //Entities have no RenderOperations of their own, relying on individual subentites for render data.
                return null;
            }
        }
        public override BoundingBox BoundingBox
        {
            get
            {
                BoundingBox merged = new BoundingBox();
                for (int i = 0; i < subEntites.Count; i++)
                {
                    merged = merged.Merge(subEntites[i].BoundingBox);
                }

                return merged;
            }
        }

        public override Material Material
        {
            get
            {
                return base.Material;
            }
            set
            {
                base.Material = value;
                for (int i = 0; i < subEntites.Count; i++)
                {
                    if (value == null)
                    {
                        subEntites[i].Material = null;
                    }
                    else
                    {
                        subEntites[i].Material = (Material)value.Clone();
                    }
                }
            }
        }
    }
}
